package tsdb

import (
	"encoding/binary"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"sync"
)

type DataPoint struct {
	Tags      map[string]string
	Timestamp int64
	Value     float64
}

type TimeSeries struct {
	Name   string
	Points []DataPoint
}

type TSDB struct {
	series map[string]*TimeSeries
	path   string
	mu     sync.RWMutex
}

func NewTSDB(path string) (*TSDB, error) {
	if err := os.MkdirAll(path, 0755); err != nil {
		return nil, err
	}

	return &TSDB{
		series: make(map[string]*TimeSeries),
		path:   path,
	}, nil
}

func (db *TSDB) Write(name string, point DataPoint) error {
	db.mu.Lock()
	defer db.mu.Unlock()

	ts, exists := db.series[name]
	if !exists {
		ts = &TimeSeries{
			Name:   name,
			Points: make([]DataPoint, 0),
		}
		db.series[name] = ts
	}

	ts.Points = append(ts.Points, point)
	if err := db.persistSeries(name, point); err != nil {
		log.Println("error occured when series persist", err)
	}
	return nil
}

// Query 查询指定时间范围的数据
func (db *TSDB) Query(name string, start, end int64) ([]DataPoint, error) {
	db.mu.RLock()
	ts, exists := db.series[name]
	db.mu.RUnlock()

	if !exists {
		return nil, fmt.Errorf("series %s not found", name)
	}

	var result []DataPoint
	for _, p := range ts.Points {
		if p.Timestamp >= start && p.Timestamp <= end {
			result = append(result, p)
		}
	}

	// 按时间戳排序
	sort.Slice(result, func(i, j int) bool {
		return result[i].Timestamp < result[j].Timestamp
	})

	return result, nil
}

// persistSeries 持久化单个数据点
func (db *TSDB) persistSeries(name string, point DataPoint) error {
	filename := filepath.Join(db.path, fmt.Sprintf("%s.dat", name))
	f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	// 写入时间戳
	if err := binary.Write(f, binary.BigEndian, point.Timestamp); err != nil {
		return err
	}

	// 写入值
	if err := binary.Write(f, binary.BigEndian, point.Value); err != nil {
		return err
	}

	return nil
}

// LoadData 加载持久化的数据
func (db *TSDB) LoadData() error {
	files, err := os.ReadDir(db.path)
	if err != nil {
		return err
	}

	for _, file := range files {
		if filepath.Ext(file.Name()) != ".dat" {
			continue
		}

		name := file.Name()[:len(file.Name())-4]
		if err := db.loadSeries(name); err != nil {
			return err
		}
	}

	return nil
}

// loadSeries 加载单个时间序列的数据
func (db *TSDB) loadSeries(name string) error {
	filename := filepath.Join(db.path, fmt.Sprintf("%s.dat", name))
	f, err := os.OpenFile(filename, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	ts := &TimeSeries{
		Name:   name,
		Points: make([]DataPoint, 0),
	}

	for {
		var timestamp int64
		var value float64

		if err := binary.Read(f, binary.BigEndian, &timestamp); err != nil {
			break
		}
		if err := binary.Read(f, binary.BigEndian, &value); err != nil {
			break
		}

		point := DataPoint{
			Timestamp: timestamp,
			Value:     value,
			Tags:      make(map[string]string),
		}

		ts.Points = append(ts.Points, point)
	}

	db.series[name] = ts
	return nil
}
